/*
 * chat_mgr.cpp
 *
 *  Created on: Dec 9, 2013
 *      Author: killerg
 */

#include "../server_deps.h"
#include "../client_session.h"
#include "../server.h"
#include "../server_console.h"

#include "chat_mgr.h"
#include "game_cfg.h"
#include "player_data.h"

#include "share/net_s.h"
#include "share/util.h"
#include "share/cs_msg.pb.h"
#include "share/msg.h"

bool chatmgr_t::init() {
	g_console->watch_n("channel_num", [this]()->int{ return channel_map.size(); });
	g_console->watch_n("channel_free", [this]()->int{ return free_channel.size(); });
	g_console->watch_n("next_channelid", [this]()->int{ return next_channelid; });
	g_console->watch_n("chat_drop_num", [this]()->int{ return drop_num; });
	g_console->watch_n("chat_queued_num", [this]()->int{
	    int total_queued = 0;
	    for (auto itr1 : channel_map) {
	        total_queued += itr1.second->send_queue.size();
	    }
	    return total_queued;
	});
    return true;
}
void chatmgr_t::destroy() {
    channel_map.for_each([](int id, channel_t* &channel){
        safe_del(channel);
    });
    for (auto &itr : free_channel) {
    	delete itr;
    }
    free_channel.clear();
}
void chatmgr_t::update() {
    int msgnum = 0;
    do {
        msgnum = 0;
        for (auto itr1:channel_map) {
            int queue_size = itr1.second->send_queue.size();
            msgnum += queue_size;
            if (queue_size == 0)
                continue;

            auto msg = itr1.second->send_queue.front();
            itr1.second->send_queue.pop();

            if (g_game_cfg->ChatHistory > 0) {
                itr1.second->log.push_back(msg);
                while (itr1.second->log.size() > g_game_cfg->ChatHistory)
                	itr1.second->log.pop_front();
            }

            void* buffer = malloc(msg.msgsize + 4);
            msg.msg->SerializeToArray((char*)buffer + 4, msg.msgsize);
            *(uint32_t*)buffer = msg.msgsize;

            for (auto itr : itr1.second->sess_vec) {
                if (itr == NULL)
                    continue;

                if (msg.exclude_playerid >= 0 && itr->playerid == msg.exclude_playerid)
                    continue;

                g_sessmgr->send_buffer(itr, msg.msg->id(), buffer, msg.msgsize + 4);
            }

            free(buffer);
        }
        return;
    } while (msgnum);
}

int chatmgr_t::add(int64_t playerid, client_session_t* sess) {
    channel_t* to_add = NULL;
    for (auto itr = channel_map.begin(); itr != channel_map.end(); ++itr) {
        auto channel = itr->second;
        if (channel->id_map.size() >= g_game_cfg->ChatChannelSize)
            continue;

        to_add = channel;
    }

    if (NULL == to_add) {
        if (!free_channel.empty()) {
        	to_add = free_channel.front();
        	free_channel.pop_front();
        } else {
            to_add = new channel_t;
            to_add->id = next_channelid++;
        }
        channel_map.set(to_add->id, to_add);
    }

    int pos = -1;
    if (!to_add->freepos_list.empty()) {
        pos = to_add->freepos_list.front();
        to_add->freepos_list.pop_front();
        to_add->sess_vec[pos] = sess;
    } else {
        pos = to_add->sess_vec.size();
        to_add->sess_vec.push_back(sess);
    }

    to_add->id_map[playerid] = pos;

    return to_add->id;
}
void chatmgr_t::remove(int chn, int64_t playerid) {
    auto channel = channel_map.get(chn);
    if (NULL == channel)
        return;

    auto itr = channel->id_map.find(playerid);
    if (itr == channel->id_map.end())
        return;

    channel->sess_vec[itr->second] = NULL;
    channel->freepos_list.push_front(itr->second);
    channel->id_map.erase(itr);
    if (channel->id_map.empty()) {
    	free_channel.push_front(channel);
    	channel_map.erase(chn);
    }
}

void chatmgr_t::send_log(sp_player_t player) {
	if (!player->sess)
		return;

	auto chn = channel_map.get(player->att(cs::Pl4n_Channel));
	if (!chn)
		return;

    char buffer[2048];
    for (auto itr : chn->log) {
        *(uint32_t*)buffer = itr.msgsize;
        itr.msg->SerializeToArray((char*)buffer + 4, itr.msgsize);

        g_sessmgr->send_buffer(player->sess, itr.msg->id(), (void*)buffer, itr.msgsize + 4);
	}
}

void chatmgr_t::add_channel(cs::SMsg* msg, int channel, int64_t exclude_playerid) {
    delay_msg_t delaymsg;
    delaymsg.msg = sp_msg_t(msg);
    delaymsg.exclude_playerid = exclude_playerid;
    delaymsg.msgsize = msg->ByteSize();

    for (auto itr1:channel_map) {
        if (channel >= 0 && itr1.first != channel)
            continue;

        if (itr1.second->send_queue.size() > 1000) {
            ++drop_num;
            continue;
        }

        itr1.second->send_queue.push(delaymsg);
    }
}

void chatmgr_t::broad_msg(sp_msg_t msg, int channel) {
	size_t size = msg->ByteSize();
    void* buffer = malloc(size + 4);
    msg->SerializeToArray((char*)buffer + 4, size);
    *(uint32_t*)buffer = size;

    broad(msg->id(), buffer, size + 4, channel);

    free(buffer);
}

void chatmgr_t::broad_scroll(const char* content, int channel) {
    CREATE_CS_SMSG(SChat, chat);
    chat->set_channel(cs::ECC_Scroll);
    chat->set_content(content);
    add_channel(___msg, channel);
}

void chatmgr_t::broad_system(const char* content, int channel) {
    CREATE_CS_SMSG(SChat, chat);
    chat->set_channel(cs::ECC_System);
    chat->set_content(content);
    add_channel(___msg, channel);
}

void chatmgr_t::broad(int32_t msgid, const void* msg, size_t size, int channel, int64_t exclude_playerid) {
    for (auto itr1:channel_map) {
        if (channel >= 0 && itr1.first != channel)
            continue;

        for (auto itr = itr1.second->sess_vec.begin(); itr != itr1.second->sess_vec.end(); ++itr) {
            if (*itr == NULL)
                continue;

            if (exclude_playerid >= 0 && (*itr)->playerid == exclude_playerid)
                continue;

            g_sessmgr->send_buffer((*itr), msgid, msg, size);
        }
    }
}
